/*
 * Copyright (C) 2005-2011 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2006-2011 ScriptDev2 <http://www.scriptdev2.com/>
 *
 * Copyright (C) 2010-2011 ProjectSkyfire <http://www.projectskyfire.org/>
 * 
 * Copyright (C) 2011 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/* ScriptData
 SDName: Instance_Blackrock_Depths
 SD%Complete: 100
 SDComment:
 SDCategory: Blackrock Depths
 EndScriptData */

#include "ScriptPCH.h"
#include "blackrock_depths.h"

#define TIMER_TOMBOFTHESEVEN    15000

enum eEnums {
	MAX_ENCOUNTER = 6,

	NPC_EMPEROR = 9019,
	NPC_PHALANX = 9502,
	NPC_ANGERREL = 9035,
	NPC_DOPEREL = 9040,
	NPC_HATEREL = 9034,
	NPC_VILEREL = 9036,
	NPC_SEETHREL = 9038,
	NPC_GLOOMREL = 9037,
	NPC_DOOMREL = 9039,
	NPC_MAGMUS = 9938,

	GO_ARENA1 = 161525,
	GO_ARENA2 = 161522,
	GO_ARENA3 = 161524,
	GO_ARENA4 = 161523,
	GO_SHADOW_LOCK = 161460,
	GO_SHADOW_MECHANISM = 161461,
	GO_SHADOW_GIANT_DOOR = 157923,
	GO_SHADOW_DUMMY = 161516,
	GO_BAR_KEG_SHOT = 170607,
	GO_BAR_KEG_TRAP = 171941,
	GO_BAR_DOOR = 170571,
	GO_TOMB_ENTER = 170576,
	GO_TOMB_EXIT = 170577,
	GO_LYCEUM = 170558,
	GO_SF_N = 174745, // Shadowforge Brazier North
	GO_SF_S = 174744, // Shadowforge Brazier South
	GO_GOLEM_ROOM_N = 170573, // Magmus door North
	GO_GOLEM_ROOM_S = 170574, // Magmus door Soutsh
	GO_THRONE_ROOM = 170575, // Throne door

	GO_SPECTRAL_CHALICE = 164869,
	GO_CHEST_SEVEN = 169243
};

class instance_blackrock_depths: public InstanceMapScript {
public:
	instance_blackrock_depths() :
			InstanceMapScript("instance_blackrock_depths", 230) {
	}

	InstanceScript* GetInstanceScript(InstanceMap* pMap) const {
		return new instance_blackrock_depths_InstanceMapScript(pMap);
	}

	struct instance_blackrock_depths_InstanceMapScript: public InstanceScript {
		instance_blackrock_depths_InstanceMapScript(Map* pMap) :
				InstanceScript(pMap) {
			Initialize();
		}
		;

		uint32 m_auiEncounter[MAX_ENCOUNTER];
		std::string str_data;

		uint64 EmperorGUID;
		uint64 PhalanxGUID;
		uint64 MagmusGUID;

		uint64 GoArena1GUID;
		uint64 GoArena2GUID;
		uint64 GoArena3GUID;
		uint64 GoArena4GUID;
		uint64 GoShadowLockGUID;
		uint64 GoShadowMechGUID;
		uint64 GoShadowGiantGUID;
		uint64 GoShadowDummyGUID;
		uint64 GoBarKegGUID;
		uint64 GoBarKegTrapGUID;
		uint64 GoBarDoorGUID;
		uint64 GoTombEnterGUID;
		uint64 GoTombExitGUID;
		uint64 GoLyceumGUID;
		uint64 GoSFSGUID;
		uint64 GoSFNGUID;
		uint64 GoGolemNGUID;
		uint64 GoGolemSGUID;
		uint64 GoThroneGUID;
		uint64 GoChestGUID;
		uint64 GoSpectralChaliceGUID;

		uint32 BarAleCount;
		uint32 GhostKillCount;
		uint64 TombBossGUIDs[7];
		uint64 TombEventStarterGUID;
		uint32 TombTimer;
		uint32 TombEventCounter;

		void Initialize() {
			memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));

			EmperorGUID = 0;
			PhalanxGUID = 0;
			MagmusGUID = 0;

			GoArena1GUID = 0;
			GoArena2GUID = 0;
			GoArena3GUID = 0;
			GoArena4GUID = 0;
			GoShadowLockGUID = 0;
			GoShadowMechGUID = 0;
			GoShadowGiantGUID = 0;
			GoShadowDummyGUID = 0;
			GoBarKegGUID = 0;
			GoBarKegTrapGUID = 0;
			GoBarDoorGUID = 0;
			GoTombEnterGUID = 0;
			GoTombExitGUID = 0;
			GoLyceumGUID = 0;
			GoSFSGUID = 0;
			GoSFNGUID = 0;
			GoGolemNGUID = 0;
			GoGolemSGUID = 0;
			GoThroneGUID = 0;
			GoChestGUID = 0;
			GoSpectralChaliceGUID = 0;

			BarAleCount = 0;
			GhostKillCount = 0;
			TombEventStarterGUID = 0;
			TombTimer = TIMER_TOMBOFTHESEVEN;
			TombEventCounter = 0;

			for (uint8 i = 0; i < 7; ++i)
				TombBossGUIDs[i] = 0;
		}

		void OnCreatureCreate(Creature* pCreature, bool /*add*/) {
			switch (pCreature->GetEntry()) {
			case NPC_EMPEROR:
				EmperorGUID = pCreature->GetGUID();
				break;
			case NPC_PHALANX:
				PhalanxGUID = pCreature->GetGUID();
				break;
			case NPC_DOOMREL:
				TombBossGUIDs[0] = pCreature->GetGUID();
				break;
			case NPC_DOPEREL:
				TombBossGUIDs[1] = pCreature->GetGUID();
				break;
			case NPC_HATEREL:
				TombBossGUIDs[2] = pCreature->GetGUID();
				break;
			case NPC_VILEREL:
				TombBossGUIDs[3] = pCreature->GetGUID();
				break;
			case NPC_SEETHREL:
				TombBossGUIDs[4] = pCreature->GetGUID();
				break;
			case NPC_GLOOMREL:
				TombBossGUIDs[5] = pCreature->GetGUID();
				break;
			case NPC_ANGERREL:
				TombBossGUIDs[6] = pCreature->GetGUID();
				break;
			case NPC_MAGMUS:
				MagmusGUID = pCreature->GetGUID();
				if (!pCreature->isAlive())
					HandleGameObject(GetData64(DATA_THRONE_DOOR), true); // if Magmus is dead open door to last boss
				break;
			}
		}

		void OnGameObjectCreate(GameObject* pGo, bool /*add*/) {
			switch (pGo->GetEntry()) {
			case GO_ARENA1:
				GoArena1GUID = pGo->GetGUID();
				break;
			case GO_ARENA2:
				GoArena2GUID = pGo->GetGUID();
				break;
			case GO_ARENA3:
				GoArena3GUID = pGo->GetGUID();
				break;
			case GO_ARENA4:
				GoArena4GUID = pGo->GetGUID();
				break;
			case GO_SHADOW_LOCK:
				GoShadowLockGUID = pGo->GetGUID();
				break;
			case GO_SHADOW_MECHANISM:
				GoShadowMechGUID = pGo->GetGUID();
				break;
			case GO_SHADOW_GIANT_DOOR:
				GoShadowGiantGUID = pGo->GetGUID();
				break;
			case GO_SHADOW_DUMMY:
				GoShadowDummyGUID = pGo->GetGUID();
				break;
			case GO_BAR_KEG_SHOT:
				GoBarKegGUID = pGo->GetGUID();
				break;
			case GO_BAR_KEG_TRAP:
				GoBarKegTrapGUID = pGo->GetGUID();
				break;
			case GO_BAR_DOOR:
				GoBarDoorGUID = pGo->GetGUID();
				break;
			case GO_TOMB_ENTER:
				GoTombEnterGUID = pGo->GetGUID();
				break;
			case GO_TOMB_EXIT:
				GoTombExitGUID = pGo->GetGUID();
				if (GhostKillCount >= 7)
					HandleGameObject(0, true, pGo);
				else
					HandleGameObject(0, false, pGo);
				break;
			case GO_LYCEUM:
				GoLyceumGUID = pGo->GetGUID();
				break;
			case GO_SF_S:
				GoSFSGUID = pGo->GetGUID();
				break;
			case GO_SF_N:
				GoSFNGUID = pGo->GetGUID();
				break;
			case GO_GOLEM_ROOM_N:
				GoGolemNGUID = pGo->GetGUID();
				break;
			case GO_GOLEM_ROOM_S:
				GoGolemSGUID = pGo->GetGUID();
				break;
			case GO_THRONE_ROOM:
				GoThroneGUID = pGo->GetGUID();
				break;
			case GO_CHEST_SEVEN:
				GoChestGUID = pGo->GetGUID();
				break;
			case GO_SPECTRAL_CHALICE:
				GoSpectralChaliceGUID = pGo->GetGUID();
				break;
			}
		}

		void SetData64(uint32 type, uint64 data) {
			sLog->outDebug(
					LOG_FILTER_TSCR,
					"TSCR: Instance Blackrock Depths: SetData64 update (Type: %u Data " UI64FMTD ")",
					type, data);

			switch (type) {
			case DATA_EVENSTARTER:
				TombEventStarterGUID = data;
				if (!TombEventStarterGUID)
					TombOfSevenReset(); //reset
				else
					TombOfSevenStart(); //start
				break;
			}
		}

		void SetData(uint32 type, uint32 data) {
			sLog->outDebug(
					LOG_FILTER_TSCR,
					"TSCR: Instance Blackrock Depths: SetData update (Type: %u Data %u)",
					type, data);

			switch (type) {
			case TYPE_RING_OF_LAW:
				m_auiEncounter[0] = data;
				break;
			case TYPE_VAULT:
				m_auiEncounter[1] = data;
				break;
			case TYPE_BAR:
				if (data == SPECIAL)
					++BarAleCount;
				else
					m_auiEncounter[2] = data;
				break;
			case TYPE_TOMB_OF_SEVEN:
				m_auiEncounter[3] = data;
				break;
			case TYPE_LYCEUM:
				m_auiEncounter[4] = data;
				break;
			case TYPE_IRON_HALL:
				m_auiEncounter[5] = data;
				break;
			case DATA_GHOSTKILL:
				GhostKillCount += data;
				break;
			}

			if (data == DONE || GhostKillCount >= 7) {
				OUT_SAVE_INST_DATA;

				std::ostringstream saveStream;
				saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1]
						<< " " << m_auiEncounter[2] << " " << m_auiEncounter[3]
						<< " " << m_auiEncounter[4] << " " << m_auiEncounter[5]
						<< " " << GhostKillCount;

				str_data = saveStream.str();

				SaveToDB();
				OUT_SAVE_INST_DATA_COMPLETE;
			}
		}

		uint32 GetData(uint32 type) {
			switch (type) {
			case TYPE_RING_OF_LAW:
				return m_auiEncounter[0];
			case TYPE_VAULT:
				return m_auiEncounter[1];
			case TYPE_BAR:
				if (m_auiEncounter[2] == IN_PROGRESS && BarAleCount == 3)
					return SPECIAL;
				else
					return m_auiEncounter[2];
			case TYPE_TOMB_OF_SEVEN:
				return m_auiEncounter[3];
			case TYPE_LYCEUM:
				return m_auiEncounter[4];
			case TYPE_IRON_HALL:
				return m_auiEncounter[5];
			case DATA_GHOSTKILL:
				return GhostKillCount;
			}
			return 0;
		}

		uint64 GetData64(uint32 data) {
			switch (data) {
			case DATA_EMPEROR:
				return EmperorGUID;
			case DATA_PHALANX:
				return PhalanxGUID;
			case DATA_ARENA1:
				return GoArena1GUID;
			case DATA_ARENA2:
				return GoArena2GUID;
			case DATA_ARENA3:
				return GoArena3GUID;
			case DATA_ARENA4:
				return GoArena4GUID;
			case DATA_GO_BAR_KEG:
				return GoBarKegGUID;
			case DATA_GO_BAR_KEG_TRAP:
				return GoBarKegTrapGUID;
			case DATA_GO_BAR_DOOR:
				return GoBarDoorGUID;
			case DATA_EVENSTARTER:
				return TombEventStarterGUID;
			case DATA_SF_BRAZIER_N:
				return GoSFNGUID;
			case DATA_SF_BRAZIER_S:
				return GoSFSGUID;
			case DATA_THRONE_DOOR:
				return GoThroneGUID;
			case DATA_GOLEM_DOOR_N:
				return GoGolemNGUID;
			case DATA_GOLEM_DOOR_S:
				return GoGolemSGUID;
			case DATA_GO_CHALICE:
				return GoSpectralChaliceGUID;
			}
			return 0;
		}

		std::string GetSaveData() {
			return str_data;
		}

		void Load(const char* in) {
			if (!in) {
				OUT_LOAD_INST_DATA_FAIL;
				return;
			}

			OUT_LOAD_INST_DATA(in);

			std::istringstream loadStream(in);
			loadStream >> m_auiEncounter[0] >> m_auiEncounter[1]
					>> m_auiEncounter[2] >> m_auiEncounter[3]
					>> m_auiEncounter[4] >> m_auiEncounter[5] >> GhostKillCount;

			for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
				if (m_auiEncounter[i] == IN_PROGRESS)
					m_auiEncounter[i] = NOT_STARTED;
			if (GhostKillCount > 0 && GhostKillCount < 7)
				GhostKillCount = 0; //reset tomb of seven event
			if (GhostKillCount >= 7)
				GhostKillCount = 7;

			OUT_LOAD_INST_DATA_COMPLETE;
		}

		void TombOfSevenEvent() {
			if (GhostKillCount < 7 && TombBossGUIDs[TombEventCounter]) {
				if (Creature* boss = instance->GetCreature(TombBossGUIDs[TombEventCounter])) {
					boss->setFaction(FACTION_HOSTILE);
					boss->RemoveFlag(UNIT_FIELD_FLAGS,
							UNIT_FLAG_OOC_NOT_ATTACKABLE);
					if (Unit *pTarget = boss->SelectNearestTarget(500))
						boss->AI()->AttackStart(pTarget);
				}
			}
		}

		void TombOfSevenReset() {
			HandleGameObject(GoTombExitGUID, false); //event reseted, close exit door
			HandleGameObject(GoTombEnterGUID, true); //event reseted, open entrance door
			for (uint8 i = 0; i < 7; ++i) {
				if (Creature* boss = instance->GetCreature(TombBossGUIDs[i])) {
					if (!boss->isAlive()) { //do not call EnterEvadeMode(), it will create infinit loops
						boss->Respawn();
						boss->RemoveAllAuras();
						boss->DeleteThreatList();
						boss->CombatStop(true);
						boss->LoadCreaturesAddon();
						boss->GetMotionMaster()->MoveTargetedHome();
						boss->SetLootRecipient(NULL);
					}
					boss->setFaction(FACTION_FRIEND);
				}
			}
			GhostKillCount = 0;
			TombEventStarterGUID = 0;
			TombEventCounter = 0;
			TombTimer = TIMER_TOMBOFTHESEVEN;
			SetData(TYPE_TOMB_OF_SEVEN, NOT_STARTED);
		}

		void TombOfSevenStart() {
			HandleGameObject(GoTombExitGUID, false); //event started, close exit door
			HandleGameObject(GoTombEnterGUID, false); //event started, close entrance door
			SetData(TYPE_TOMB_OF_SEVEN, IN_PROGRESS);
		}

		void TombOfSevenEnd() {
			DoRespawnGameObject(GoChestGUID, DAY);
			HandleGameObject(GoTombExitGUID, true); //event done, open exit door
			HandleGameObject(GoTombEnterGUID, true); //event done, open entrance door
			TombEventStarterGUID = 0;
			SetData(TYPE_TOMB_OF_SEVEN, DONE);
		}
		void Update(uint32 diff) {
			if (TombEventStarterGUID && GhostKillCount < 7) {
				if (TombTimer <= diff) {
					TombTimer = TIMER_TOMBOFTHESEVEN;
					++TombEventCounter;
					TombOfSevenEvent();
					// Check Killed bosses
					for (uint8 i = 0; i < 7; ++i) {
						if (Creature* boss = instance->GetCreature(TombBossGUIDs[i])) {
							if (!boss->isAlive()) {
								GhostKillCount = i + 1;
							}
						}
					}
				} else
					TombTimer -= diff;
			}
			if (GhostKillCount >= 7 && TombEventStarterGUID)
				TombOfSevenEnd();
		}
	};
};

void AddSC_instance_blackrock_depths() {
	new instance_blackrock_depths();
}
